<?php
/**
 * 阿里云的OSS工具类
 * Created by PhpStorm.
 * User: youqingsong
 * Date: 2022/02/17
 * Time: 15:24
 */

namespace Ktnw\OssSupport\Utils;

use DateTime;
use DateTimeInterface;
use Illuminate\Support\Str;
use OSS\Core\OssException;

/**
 * 上传OSS的工具
 */
class OssUtils
{
    /**
     * put object to oss
     * @param $bucket string 存储空间名称
     * @param object 文件名称
     * @param $content string 要上传的文件内容
     * @return array
     * @throws OssException
     */
    public static function putObjectToOss(string $bucket, $object, string $content): array
    {
        $data = [];
        $info = OssClientSingleton::getInstance()->putObject($bucket, $object, $content);
        if (!empty($info['info']['url'])) {
            // 上传成功
            $data['message']  = '成功';
            $data['status']   = 'success';
            $data['fileName'] = $object;
            $data['url']      = self::getOssAccessUrl() . $object;
        } else {
            // 上传失败
            $data['status']  = 'fail';
            $data['message'] = '上传失败';
        }
        return $data;
    }

    /**
     * put local file to oss
     * @param $bucket string 存储空间名称
     * @param $object string 文件名称
     * @param $filePath string 文件路径 由本地文件路径加文件名包括后缀组成 例如/users/local/myfile.txt
     * @return array
     */
    public static function putFileToOss(string $bucket, string $object, string $filePath): array
    {
        $data = [];
        try {
            $info = OssClientSingleton::getInstance()->uploadFile($bucket, $object, $filePath);
            if (!empty($info['info']['url'])) {
                // 上传成功
                $data['message']  = '成功';
                $data['status']   = 'success';
                $data['fileName'] = $object;
                $url              = self::getOssAccessUrl();
                $data['url']      = Str::endsWith($url, "/") ? $url . $object : $url . "/" . $object;
            } else {
                // 上传失败
                $data['status']  = 'fail';
                $data['message'] = '上传失败';
            }
        } catch (OssException $e) {
            $data['status']  = 'fail';
            $data['message'] = $e->getMessage();
        }
        return $data;
    }

    /**
     * 判断oss中是否存在文件
     * @param string $bucket
     * @param string $object
     * @return boolean
     * @throws OssException
     */
    public static function doesObjectExist(string $bucket, string $object): bool
    {
        return OssClientSingleton::getInstance()->doesObjectExist($bucket, $object);
    }

    /**
     * 删除oss中的文件
     * @param string $bucket
     * @param array $objects array 数组
     * @return array
     */
    public static function deleteObjects(string $bucket, array $objects): array
    {
        $data = [];
        try {
            OssClientSingleton::getInstance()->deleteObjects($bucket, $objects);
            $data['status']  = 'success';
            $data['message'] = "";
        } catch (\Exception $e) {
            $data['status']  = 'fail';
            $data['message'] = $e->getMessage();
        }
        return $data;
    }

    /**
     * 获取图片的签名访问链接
     * @param string $bucket
     * @param $object string 文件名
     * @param float|int $timeout int 超时时间(单位:秒)
     * @param string $method
     * @param array $options
     * @return string 可访问的url
     * @throws OssException
     */
    public static function getSignUrl(string $bucket, string $object, $timeout = 2 * 60 * 60, string $method = "GET", array $options = []): string
    {
        return OssClientSingleton::getInstance()->signUrl($bucket, $object, $timeout, $method, $options);
    }

    /**
     * 前端web使用postObject上传文件到OSS的签名时，需要用到的时间
     * @param $time
     * @return string
     * @throws
     */
    public static function gmt_iso8601($time): string
    {
        $dtStr      = date("c", $time);
        $myDatetime = new DateTime($dtStr);
        $expiration = $myDatetime->format(DateTimeInterface::ISO8601);
        $pos        = strpos($expiration, '+');
        $expiration = substr($expiration, 0, $pos);
        return $expiration . "Z";
    }


    /**
     * oss中的文件拷贝
     *
     * @param string $from_bucket
     * @param string $from_object
     * @param string $to_bucket
     * @param string $to_object
     * @param array $option
     * @return string
     * @throws OssException
     */
    public static function copyObject(string $from_bucket, string $from_object, string $to_bucket, string $to_object, array $option = []): string
    {
        $r = OssClientSingleton::getInstance()->copyObject($from_bucket, $from_object, $to_bucket, $to_object, $option);
        return empty($r["info"]["url"]) ? "" : $r["info"]["url"];
    }

    /**
     * 返回oss文件名
     * @param string $ossUrl
     * @return string
     */
    public static function fetchOssFileName(string $ossUrl): string
    {
        if (empty($ossUrl)) {
            return "";
        }
        return str_replace([
            "http:",
            "https:",
            self::getOssAccessUrl() . "/",
            "//ktnw-sgzx.oss-cn-beijing.aliyuncs.com/"
        ], "", $ossUrl);
    }

    /**
     * 获取完整的oss文件url
     * @param string $ossFileUrl
     * @return string
     */
    public static function fetchOssFileUrl(string $ossFileUrl): string
    {
        $ossFileName = self::fetchOssFileName($ossFileUrl);
        $url         = self::getOssAccessUrl();
        $url         = Str::startsWith($url, "http") ? $url : "https:";
        return $url . "/" . $ossFileName;
    }


    /**
     * 前端oss上传的签名
     * @param int $upType 上传目录类型 1-上传到公用目录，可直接访问.2-上传到私有目录，需授权后访问。
     * @param string $catalogue 上传的目录
     * @param int $expire 设置该policy超时时间. 即这个policy过了这个有效时间，将不能访问。单位：秒.
     */
    public static function ossSignature(int $upType, string $catalogue, int $expire = 30 * 60): array
    {
        $catalogue = empty($catalogue) ? str_replace(["-"], "", self::getCurDateStr()) : $catalogue;
        $id        = self::getOssAccessKeyId();
        $key       = self::getOssAccessKeySecret();

        // $host的格式为 bucketname.endpoint，请替换为您的真实信息。
        $host       = "//" . self::getOssBucketName() . '.' . self::getOssEndpoint();
        $dir        = $upType == 1 ? self::getPubAccessCatalog() : '';
        $dir        .= $catalogue;
        $now        = time();
        $end        = $now + $expire;
        $expiration = self::gmt_iso8601($end);

        //最大文件大小. 用户可以自己设置. 单位:M
        $maxSize = self::getMaxSize();
        $maxSize = empty($maxSize) ? 30 : $maxSize;

        $condition    = [0 => 'content-length-range', 1 => 0, 2 => $maxSize * 1024 * 1024];
        $conditions[] = $condition;

        // 表示用户上传的数据，必须是以$dir开始，不然上传会失败，这一步不是必须项，只是为了安全起见，防止用户通过policy上传到别人的目录。
        $start        = [0 => 'starts-with', 1 => '$key', 2 => $dir];
        $conditions[] = $start;

        $arr            = ['expiration' => $expiration, 'conditions' => $conditions];
        $policy         = json_encode($arr);
        $base64_policy  = base64_encode($policy);
        $string_to_sign = $base64_policy;
        $signature      = base64_encode(hash_hmac('sha1', $string_to_sign, $key, true));

        $response              = [];
        $response['accessid']  = $id;
        $response['ossUrl']    = self::getOssAccessUrl();
        $response['url']       = $host;
        $response['policy']    = $base64_policy;
        $response['signature'] = $signature;
        $response['expire']    = $end;
        $response['dir']       = $dir;  // 这个参数是设置用户上传文件时指定的前缀。
        return $response;
    }

    /**
     * 获取当前的日期字符串
     */
    private static function getCurDateStr()
    {
        return date("Y-m-d", time());
    }


    private static function getOssAccessUrl()
    {
        return config("ossConfig.oss_access_url");
    }

    private static function getOssAccessKeyId()
    {
        return config("ossConfig.oss_access_key_id");
    }

    private static function getOssAccessKeySecret()
    {
        return config("ossConfig.oss_access_key_secret");
    }

    private static function getOssBucketName()
    {
        return config("ossConfig.oss_bucket_name");
    }

    private static function getOssEndpoint()
    {
        return config("ossConfig.oss_endpoint");
    }

    private static function getPubAccessCatalog()
    {
        return config("ossConfig.pub_access_catalog");
    }

    private static function getMaxSize()
    {
        return config("ossConfig.max_size");
    }


}